﻿using System;
using System.Collections.Generic;
using System.Net;

namespace Business.Entities
{
    [Serializable]
    public class CookieCollection : List<Cookie>
    {
        /// <summary>
        /// Returns the header string for this list of cookies
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string s = "";
            for (int i = 0; i < Count; i++)
            {
                var cookie = this[i];
                s += cookie.Name + "=" + cookie.Value;
                if (i != Count - 1) s += ";";
            }
            return s;
        }

        /// <summary>
        /// Replaces or inserts key-value-domain set
        /// </summary>
        /// <param name="domain"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void ReplaceKey(string domain, string key, string value)
        {
            RemoveKey(key);
            Add(new Cookie(key, value, domain, "/"));
        }


        /// <summary>
        /// Returns the header string for this list of cookies for the specified url
        /// </summary>
        /// <param name="url">Url to filter for</param>
        /// <returns></returns>
        public string ToStringFor(string url)
        {
            string s = "";
            for (int i = 0; i < Count; i++)
            {
                var cookie = this[i];
                if (cookie.AppliesFor(url))
                {
                    s += cookie.Name + "=" + cookie.Value;
                    if (i != Count - 1) s += ";";
                }
            }
            return s;
        }

        public const string LocaleCookie = "GoogleAccountsLocale_session";
        /// <summary>
        /// Adds the cookies in the header to the list of cookies
        /// Filters out CAL-cookie
        /// Forces US locale
        /// </summary>
        /// <param name="response">WebResponse</param>
        /// <returns>New cookies added</returns>
        public CookieCollection SetCookiesFiltered(WebResponse response)
        {
            var newCookies = SetCookiesFromResponse(response);
            //ReplaceCookieValue(LocaleCookie, "en-AU");
            //newCookies.ReplaceCookieValue(LocaleCookie, "en-AU");
            RemoveKey("CAL");
            newCookies.RemoveKey("CAL");
            return newCookies;
        }

        /// <summary>
        /// Replaces the value of the cookie with the specified key with the new value.
        /// If cookie does not exist, no action is taken
        /// </summary>
        /// <param name="name">Cookie key</param>
        /// <param name="newValue">The new value</param>
        private void ReplaceCookieValue(string name, string newValue)
        {
            foreach (Cookie cookie in this)
            {
                if (cookie.Name == name) cookie.Value = newValue;
            }
        }


        private void RemoveKey(Cookie keyCookie)
        {
            Cookie toRemove = null;
            foreach (Cookie cookie in this)
            {
                if (cookie.Name == keyCookie.Name &&
                    cookie.Path == keyCookie.Path &&
                    cookie.Domain == keyCookie.Domain) toRemove = cookie;
            }
            if (toRemove != null) Remove(toRemove);
        }

        private void RemoveKey(string key)
        {
            Cookie toRemove = null;
            foreach (Cookie cookie in this)
            {
                if (cookie.Name == key) toRemove = cookie;
            }
            if (toRemove != null) Remove(toRemove);
        }

        /// <summary>
        /// Adds the cookies in the header to the list of cookies
        /// </summary>
        /// <param name="response"></param>
        /// <returns>New cookies added</returns>
        public CookieCollection SetCookiesFromResponse(WebResponse response)
        {
            var newCookies = new CookieCollection();
            string[] sqs = response.Headers.GetValues("Set-Cookie");
            if (sqs != null)
            {
                foreach (string sq in sqs)
                {
                    var tempCookies = AddCookiesFromString(sq, response.ResponseUri.Host, false);
                    newCookies.AddRange(tempCookies);
                }
            }
            return newCookies;
        }

        /// <summary>
        /// Takes a stirng on format "X=Y;Z=W" and turns into cookie objects
        /// </summary>
        /// <param name="sq"></param>
        public CookieCollection AddCookiesFromString(string sq, string defaultDomain, bool multipleCookies)
        {
            var newCookies = new CookieCollection();
            var elems = sq.Split(';');
            Cookie cookie = null;
            bool addIt = true;
            for (int i = 0; i < elems.Length; i++)
            {
                string elem = elems[i];
                if (elem.StartsWith("Path="))
                {
                    string[] namVal = elem.Split('=');
                    if (namVal.Length > 1 && cookie != null) cookie.Path = namVal[1];
                }
                else if (elem.StartsWith("Expires"))
                {
                    // TBI
                }
                else if (elem.StartsWith("Domain="))
                {
                    string[] namVal = elem.Split('=');
                    if (namVal.Length > 1 && cookie != null) cookie.Domain = namVal[1];
                    //if (namVal.Length > 1 && !namVal[1].Contains(".google.com"))
                    //{
                    //    // Wrong domain
                    //    addIt = false;
                    //}
                }
                // Name-value
                if ((i == 0 || multipleCookies) && elem.Contains("="))
                {
                    //string[] namVal = elem.Split(new []{'='},2);
                    string[] namVal = SplitMaxTwo('=', elem);
                    if (multipleCookies && cookie != null)
                    {
                        RemoveKey(cookie);
                        if (cookie.Value != "EXPIRED") Add(cookie);
                        cookie = null;
                    }
                    if (cookie == null)
                    {
                        cookie = new Cookie(namVal[0].Trim(), namVal[1].Trim());
                        cookie.Domain = defaultDomain; 
                    }
                }
            }
            if (addIt && cookie != null)
            {
                RemoveKey(cookie);
                if (cookie.Value != "EXPIRED") Add(cookie);
                newCookies.Add(cookie);
            }
            return newCookies;
        }

        /// <summary>
        /// .NET CE does not support split with max param
        /// </summary>
        /// <param name="split"></param>
        /// <param name="elem"></param>
        /// <returns></returns>
        private static string[] SplitMaxTwo(char split, string elem)
        {
            string[] temp = elem.Split(split);
            if (temp.Length <= 2) return temp;
            string[] two = new string[2];
            two[0] = temp[0];
            two[1] = temp[1];
            for (int i = 2; i < temp.Length; i++)
            {
                two[1] += split + temp[i];
            }
            return two;
        }

        private Cookie GetCookie(string key)
        {
            foreach (Cookie cookie in this) if (key == cookie.Name) return cookie;
            return null;
        }

        internal CookieCollection CloneShallow()
        {
            CookieCollection col = new CookieCollection();
            foreach (var elem in this) col.Add(elem);
            return col;
        }
    }
}
